/*
 * Copyright (C) 2015, apexes.net. All rights reserved.
 *
 *        http://www.apexes.net
 *
 */
package net.apexes.wsonrpc.server.support;

import net.apexes.wsonrpc.core.WebSocketSession;
import net.apexes.wsonrpc.core.WsonrpcConfig;
import net.apexes.wsonrpc.server.WsonrpcServerBase;
import net.apexes.wsonrpc.server.WsonrpcServerBuilder;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.PingMessage;
import org.springframework.web.socket.PongMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;

import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * 基于Spring WebSocket 的服务端
 *
 * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
 */
public class SpringWsonrpcServerHandler implements WebSocketHandler {

    protected final WsonrpcServerBase serverBase;

    public SpringWsonrpcServerHandler() {
        this(WsonrpcServerBuilder.defaultConfig());
    }

    public SpringWsonrpcServerHandler(WsonrpcConfig config) {
        this(new WsonrpcServerBase(config));
    }

    public SpringWsonrpcServerHandler(WsonrpcServerBase serverBase) {
        this.serverBase = serverBase;
    }

    @Override
    public void afterConnectionEstablished(org.springframework.web.socket.WebSocketSession session) throws Exception {
        serverBase.onOpen(new SpringWebSocketSessionAdapter(session));
    }

    @Override
    public void handleMessage(org.springframework.web.socket.WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        if (message instanceof BinaryMessage) {
            handleBinaryMessage(session, (BinaryMessage) message);
        } else if (!(message instanceof PongMessage)) {
            throw new IllegalStateException("Unexpected WebSocket message type: " + message);
        }
    }

    protected void handleBinaryMessage(org.springframework.web.socket.WebSocketSession session, BinaryMessage message) throws Exception {
        serverBase.onMessage(session.getId(), message.getPayload());
    }

    @Override
    public void afterConnectionClosed(org.springframework.web.socket.WebSocketSession session, CloseStatus status) throws Exception {
        serverBase.onClose(session.getId());
    }

    @Override
    public void handleTransportError(org.springframework.web.socket.WebSocketSession session, Throwable exception) throws Exception {
        serverBase.onError(session.getId(), exception);
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

    protected String sessionId(org.springframework.web.socket.WebSocketSession session) {
        return session.getId();
    }

    /**
     * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
     */
    private class SpringWebSocketSessionAdapter implements WebSocketSession {

        private final org.springframework.web.socket.WebSocketSession session;

        SpringWebSocketSessionAdapter(org.springframework.web.socket.WebSocketSession session) {
            this.session = session;
        }

        @Override
        public String getId() {
            return sessionId(session);
        }

        @Override
        public boolean isOpen() {
            return session.isOpen();
        }

        @Override
        public void sendBinary(byte[] bytes) throws IOException {
            session.sendMessage(new BinaryMessage(bytes));
        }

        @Override
        public void ping(byte[] bytes) throws IOException {
            PingMessage message;
            if (bytes == null || bytes.length == 0) {
                message = new PingMessage();
            } else {
                message = new PingMessage(ByteBuffer.wrap(bytes));
            }
            session.sendMessage(message);
        }

        @Override
        public void close() throws IOException {
            session.close();
        }

    }

}
